/*
 * TODO:
 * 	Created current held card
 */


package casino;

import java.awt.*;
import java.awt.event.*;
import java.awt.font.TextLayout;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.io.File;
import java.io.IOException;
import java.net.*;
import java.util.Arrays;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.imageio.ImageIO;
import javax.swing.JPanel;

import util.BuildGroup;
import util.CardValues;
import containers.*;

public class Casino extends JPanel implements Runnable, ImageObserver{
	public static volatile boolean isRunning = false; //boolean to tell if the game is running
	public static volatile boolean paused = true; //boolean to tell if the game is paused
	public static volatile boolean gameOver = true; //tells if the game is over or not
	public static final long serialVersionUID = 1L; //serial version
	public static final int DELAYS_PER_YIELD = 10;
	public Thread animator; //thread of this class
	public int fps = 30;  //desired fps
	public Image buffer = null; //buffer for painting to screen
	public Graphics2D bufGraphics; //graphics for the buffer
	public int period = 1000000000/fps;  //time to sleep in order to reach desired fps
	public static final int MAX_FRAME_SKIPS = 5;  //max frame skips
	public int pWidth = 1100;
	public int pHeight = 900;
	public Point mousePos = new Point();
	public Container parent;
	public Insets frameInsets;
	public GameCard card;
	public Point relMousePos = new Point();
	public boolean holding;
	public Point offset = new Point();
	public BufferedImage back;
	public BufferedImage felt;
	public CopyOnWriteArrayList<GameCard> cards = new CopyOnWriteArrayList<GameCard>();
	public BuildGroup builds;
	public Color trans =  new Color(0,0,0,100);
	public Color redTrans = new Color(200,0,0,100);
	public ActionMenu buildMenu;
	public MainMenu main;
	public GameCard hCard;
	public Build hBuild;
	public GameCard sCard;
	public Build sBuild;
	public Player p1;
	public Player p2;
	public Player current;
	public Font large;
	public int index = 0;
	public CopyOnWriteArrayList<Message> messages = new CopyOnWriteArrayList<Message>();
	public TextLayout temp;
	public Font font;
	
	public Casino(){
		setBackground(Color.white);
		setPreferredSize(new Dimension(pWidth, pHeight));
		setFocusable(true); //allows JPanel to recieve actions
		requestFocus();
		readyForKeyPress();  //make sure to add in termination code
		readyForMousePress();
		initializeComponents();
		//start game
		isRunning = true;
		gameOver = false;
		paused = false;
	}
	
	public void checkHosts(String subnet){
		   int timeout=50;
		   for (int i=1;i<254;i++){
			   System.out.println(i);
		       String host=subnet + "." + i;
		       try{
		    	   if (InetAddress.getByName(host).isReachable(timeout)){
		    		   System.out.println(host + " is reachable");
		    	   }
		       }catch(UnknownHostException e){
		    	   System.err.println("Unknown Host exception caught:  ");
		    	   e.printStackTrace();
		       }catch(IOException e){
		    	   System.err.println("IO exception caught:  ");
		    	   e.printStackTrace();
		       }
		   }
		}
	
	private void initializeComponents(){
		try{
			large = Font.createFont(Font.TRUETYPE_FONT, new File("images/space.ttf"));
		}catch (Exception e){}
		large = large.deriveFont(0, 70);
		buffer = createImage(pWidth, pHeight);
		p1 = new HumanPlayer(this);
		p2 = new HumanPlayer(this);
		builds = new BuildGroup(this, 300, 380, 500);
		p1.setHand(new SortedHand(this, p1, 300, 700, 500));
		p1.setDeck(new Deck(this, p1, CardValues.cards, 100, 380));
		p1.deck.shuffle();
		cards.addAll(p1.deck.cards);
		p1.setDiscard(new Discard(this, p1, 100, 700));
		p2.setHand(new SortedHand(this, p2, 300, 70, 500));
		p2.setDeck(new Deck(this, p2, p1.deck.deal(26), 920, 380));
		p2.setDiscard(new Discard(this, p2, 920, 70));
		for(int i = 0; i < 5; i++){
			p1.hand.add(p1.deck.draw());
			p2.hand.add(p2.deck.draw());
		}
		current = p1;
		p1.takeTurn();
		buildMenu = new ActionMenu(this);
		main = new MainMenu(this);
		holding = false;
		try{
			felt = ImageIO.read(new File("images/felt.jpg"));
		}catch (IOException e){}
	}

	//adds the canvas to the client, then automatically starts it
	public void addNotify(){
		super.addNotify(); //JPanel addNotify (creates peer for the canvas)
		startGame();  //begin the game
	}	
		
	private void startGame(){
		//if the game hasn't already started, start it
		if(animator==null || !isRunning){
			animator = new Thread(this);
			animator.start();
		}
	}
		
	public void run(){
		long before, after, timeDiff, sleepTime;  //used in maintaining desired fps/ups
		long overSleepTime = 0L; //amount of time machine overslept
		int delays = 0; //counts number of delays(when machine didn't sleep)
		long extraTime = 0L; //how much time the machine overused for updating/rendering
		int skips = 0; //current number of skips
		sleepTime = (long)1000L/fps;
		parent = getTopLevelAncestor();
		frameInsets = parent.getInsets();
		
		while(isRunning){
			while(!gameOver){
				//update/render/paint the game
				before = System.nanoTime();
				if(!paused){
					updateGame();
					renderGame();
				}else{
					updateMain();
					renderMain();
				}
				paintGame();
				after = System.nanoTime();
				timeDiff = after-before;
				sleepTime = (long)(period - timeDiff) - overSleepTime; //calculate desired time to sleep
				if(sleepTime>0){
					try{
						Thread.sleep(sleepTime/1000000L);  //sleep
					}catch (InterruptedException e){}
					overSleepTime = (long)(System.nanoTime()-after)-sleepTime; //calculate how long the machine overslept
				}else{
					extraTime -= sleepTime;
					overSleepTime = 0L;
					System.out.println(index++);
					//allow other essential threads to execute
					if(++delays >= DELAYS_PER_YIELD){
						Thread.yield();
						delays = 0;
					}
				}
				//update without rendering if the whole process takes too long
				while((extraTime>=period) && (skips<=MAX_FRAME_SKIPS)){
					updateGame();
					extraTime -= period;
					skips++;
				}
				skips = 0;
			}
		}
		System.exit(0);
	}
	
	public void updateMain(){
		updateMouse();
		main.updateMenu();
	}
	
	public void renderMain(){
		if(preDraw()) main.draw(bufGraphics);
	}
	
	public void updateMouse(){
		mousePos.setLocation(MouseInfo.getPointerInfo().getLocation());
		relMousePos.setLocation(mousePos.x-parent.getX()-frameInsets.left, mousePos.y-parent.getY()-frameInsets.top);
	}
	
	public void updateGame(){
		updateMouse();
		mouseHover(relMousePos);
		for(Message m: messages){
			if(m != null){
				m.update();
			}
		}
		if(current.choosing){
			buildMenu.updateMenu();
		}else if(holding){
			card.setPosition(relMousePos.x-offset.x, relMousePos.y-offset.y);
		}
		current.update();
	}
	
	private boolean preDraw(){
		if(buffer == null){
			buffer = createImage(pWidth, pHeight);
			return false;
		}else{
			bufGraphics = (Graphics2D)buffer.getGraphics();
			bufGraphics.setColor(Color.white);
			bufGraphics.drawImage(felt, 0, 0, pWidth, pHeight, this);
			return true;
		}
	}
	
	public void renderGame(){
		if(preDraw()){
			bufGraphics.setColor(trans);
			bufGraphics.fillRect(p1.hand.x-5, p1.hand.y-5, p1.hand.width+10, p1.hand.height+10);
			bufGraphics.fillRect(p2.hand.x-5, p2.hand.y-5, p2.hand.width+10, p2.hand.height+10);
			bufGraphics.setColor(Color.black);
			bufGraphics.drawRect(p1.hand.x-5, p1.hand.y-5, p1.hand.width+10, p1.hand.height+10);
			bufGraphics.drawRect(p2.hand.x-5, p2.hand.y-5, p2.hand.width+10, p2.hand.height+10);
			bufGraphics.setFont(large);
			if(current.equals(p1)){
				bufGraphics.drawString("Current Player: Player 1", 30, 50);
			}else{
				bufGraphics.drawString("Current Player: Player 2", 30, 50);
			}
			for(GameCard i: cards){
				i.draw(bufGraphics);
				if(i.equals(hCard)){
					bufGraphics.setColor(redTrans);
					bufGraphics.fillRect(i.x, i.y, i.width, i.height);
				}
			}
			if(current.choosing){
				buildMenu.draw(bufGraphics);
			}
			bufGraphics.setFont(large);
			for(Message m: messages){
				if(m != null){
					bufGraphics.setColor(m.drawColor());
					bufGraphics.drawString(m.message(), 100, 300);
				}
			}
		}	
	}
	
	public void paintGame(){
		//paints buffer to screen
		Graphics g;
		try{
			g = this.getGraphics();
			//if both can be painted
			if((g != null) && (buffer != null)){
				g.drawImage(buffer, 0, 0, null);
			}else{}
			g.dispose();
		}catch (Exception e){
			System.out.println("Graphics error: "+e);
		}
	}
	
	public void turnEnd(Player p){
		if(p.equals(p1)){
			p2.takeTurn();
			current = p2;
		}else{
			p1.takeTurn();
			current = p1;
		}
	}
	
	public void addMessage(String s){
			messages.add(new Message(this, s, 40));
	}
	
	private void readyForKeyPress(){
		addKeyListener(new KeyAdapter(){//use keyboard to allow for exit
			public void keyPressed(KeyEvent e){
				if(current.choosing){
					buildMenu.keyPress(e.getKeyCode());
				}else{
					keyPress(e.getKeyCode());
				}
			}
			public void keyReleased(KeyEvent e){
				if(!current.choosing){
					keyRelease(e.getKeyCode());
				}
			}
		});
	}
	
	private void readyForMousePress(){
		addMouseListener(new MouseAdapter(){ //allow the game to deal with mouse presses
			public void mousePressed(MouseEvent e){
				if(paused){
					main.mousePress(e);
				}else{
					if(current.choosing){
						buildMenu.mousePress(e);
					}else{
						mousePress(e.getX(), e.getY());
					}
				}
			}
			public void mouseReleased(MouseEvent e){
				if(paused){
					main.mouseRelease(e);
				}else{
					if(current.choosing){
						buildMenu.mouseRelease(e);
					}else{
						mouseRelease(e.getX(), e.getY());
					}
				}
			}
		});
	}
	
	private void mouseHover(Point p){
		if(current.building || current.taking || current.duplicating){
			if(sCard == null){
				for(int i = cards.size()-1; i >= 0; i--){
					if(cards.get(i).contains(p) && cards.get(i).accesible){
						hCard = cards.get(i);
						return;
					}
				}
			}else if(sBuild == null){
				for(int i = builds.size()-1; i >= 0; i--){
					if(builds.get(i).contains(p)){
						hBuild = builds.get(i);
						return;
					}
				}
			}
		}else if(current.adding){
			if(sCard == null){
				for(int i = cards.size()-1; i >= 0; i--){
					if(cards.get(i).contains(p) && cards.get(i).accesible){
						hCard = cards.get(i);
						return;
					}
				}
			}
		}
		hCard = null;
		hBuild = null;
	}
	
	private void keyPress(int keycode){
		if(keycode == KeyEvent.VK_ESCAPE){
			paused = true;
			gameOver = true;
			isRunning = false;
		}else if(keycode == KeyEvent.VK_Q){
			pauseGame();
		}
	}
	
	private void keyRelease(int keycode){}
	
	private void mousePress(int x, int y){
		if(current.adding){
			sCard = hCard;
		}else if(current.taking || current.building || current.duplicating){
			if(sCard == null){
				sCard = hCard;
			}else{
				sBuild = hBuild;
			}
		}else{
			for(int i = cards.size()-1; i >= 0; i--){
				if(cards.get(i).contains(x, y) && cards.get(i).accesible){
					card = cards.get(i);
					if(card.parent != null){
						card.parent.remove(card);
					}
					offset.setLocation(x-card.x, y-card.y);
					bringToFront(card);
					holding = true;
					return;
				}
			}
			card = null;
		}
	}
	
	private void mouseRelease(int x, int y){
		holding = false;
		if(card != null){
			if(card.intersects(current.hand)){
				current.hand.add(card);
			}else{
				for(Build b: builds){
					if(card.intersects(b)){
						b.add(card);
						card = null;
						return;
					}
				}
				card.parent.add(card);
			}
		}
		card = null;
	}
	
	public boolean isBuild(Build b){
		if(b == null){
			return false;
		}
		for(Build i: builds){
			if(b.equals(i)){
				return true;
			}
		}
		return false;
	}
	
	public void displayMessage(String s){
		
	}
	
	public void pauseGame(){
		paused = true;
	}
	
	public void resumeGame(){
		paused = false;
	}
	
	public void quitGame(){
		paused = true;
		gameOver = true;
		isRunning = false;
	}
	
	public void bringToFront(int index){
		cards.add(cards.remove(index));
	}
	
	public void bringToFront(GameCard c){
		cards.remove(c);
		cards.add(c);
	}
	
	public void sendToBack(GameCard c){
		cards.remove(c);
		cards.add(0, c);
	}
}
